// addrspace.cc 
//	Routines to manage address spaces (executing user programs).
//
//	In order to run a user program, you must:
//
//	1. link with the -N -T 0 option 
//	2. run coff2noff to convert the object file to Nachos format
//		(Nachos object code format is essentially just a simpler
//		version of the UNIX executable object code format)
//	3. load the NOFF file into the Nachos file system
//		(if you haven't implemented the file system yet, you
//		don't need to do this last step)
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "addrspace.h"
#include "noff.h"
#include "bitmap.h"
#include "syscall.h"
#ifdef HOST_SPARC
#include <strings.h>


#endif
unsigned int size, FindVar;
char *machineLocation;
SpaceId SpaceInt;
OpenFile *executableCopy;
NoffHeader noffH;

//----------------------------------------------
// AddrSpace::initSpaceCount;
// set initSpaceCount = 0;
//----------------------------------------------
void
AddrSpace::initSpaceId()
{
	SpaceInt = 0;
};


//----------------------------------------------
// AddrSpace::GetSpaceId()
//
// return the space id for the function
//----------------------------------------------
SpaceId
AddrSpace::GetSpaceId()
{
	return SpaceIdInt;
	
};

//------------------------------------------------
// AddrSpace::SetSpaceId()
// setter for the space id
//------------------------------------------------
void
AddrSpace::incrementSpaceId()
{
	SpaceIdInt++;
};



//----------------------------------------------------------------------
// SwapHeader
// 	Do little endian to big endian conversion on the bytes in the 
//	object file header, in case the file was generated on a little
//	endian machine, and we're now running on a big endian machine.
//----------------------------------------------------------------------

static void 
SwapHeader (NoffHeader *noffH)
{
	noffH->noffMagic = WordToHost(noffH->noffMagic);
	noffH->code.size = WordToHost(noffH->code.size);
	noffH->code.virtualAddr = WordToHost(noffH->code.virtualAddr);
	noffH->code.inFileAddr = WordToHost(noffH->code.inFileAddr);
	noffH->initData.size = WordToHost(noffH->initData.size);
	noffH->initData.virtualAddr = WordToHost(noffH->initData.virtualAddr);
	noffH->initData.inFileAddr = WordToHost(noffH->initData.inFileAddr);
	noffH->uninitData.size = WordToHost(noffH->uninitData.size);
	noffH->uninitData.virtualAddr = WordToHost(noffH->uninitData.virtualAddr);
	noffH->uninitData.inFileAddr = WordToHost(noffH->uninitData.inFileAddr);
}

//----------------------------------------------------------------------
// AddrSpace::AddrSpace
// 	Create an address space to run a user program.
//	Load the program from a file "executable", and set everything
//	up so that we can start executing user instructions.
//
//	Assumes that the object code file is in NOFF format.
//
//	First, set up the translation from program memory to physical 
//	memory.  For now, this is really simple (1:1), since we are
//	only uniprogramming, and we have a single unsegmented page table
//
//	"executable" is the file containing the object code to load into memory
//----------------------------------------------------------------------

AddrSpace::AddrSpace(OpenFile *executable)
{
	//NoffHeader noffH;
	
    unsigned int i;
	// test for true to initialize testSpaceId
	// else incrementSpae
	if(testSpace == TRUE)
	{
		initSpaceId();
		testSpace = FALSE;
	}
	else
	{
		incrementSpaceId();
	};
	
	executableCopy = executable;
    executable->ReadAt((char *)&noffH, sizeof(noffH), 0);
    if ((noffH.noffMagic != NOFFMAGIC) && 
		(WordToHost(noffH.noffMagic) == NOFFMAGIC))
    	SwapHeader(&noffH);
    ASSERT(noffH.noffMagic == NOFFMAGIC);
//----------------------------------------------------------------------
//  increment the SpaceId with each process

	SpaceId four;
	four = GetSpaceId();
	printf(" SpaceId = %d \n",four);
//----------------------------------------------------------------------	
// how big is address space?
    size = noffH.code.size + noffH.initData.size + noffH.uninitData.size 
			+ UserStackSize;	// we need to increase the size
	
	
						// to leave room for the stack
    printf(" size of the address space %d size -- page size %d \n",size,PageSize);
    numPages = divRoundUp(size, PageSize);
    size = numPages * PageSize;
    printf(" size of the address space %d size number of pages %d \n",size,numPages);
    
    ASSERT(numPages <= NumPhysPages);		// check we're not trying
						// to run anything too big --
						// at least until we have
						// virtual memory
	
	
	//four =  GetSpaceId();
	//printf("spaceid = %d",four);
    DEBUG('a', "Initializing address space, num pages %d, size %d\n", 
					numPages, size);
// first, set up the translation 
    pageTable = new TranslationEntry[numPages];

	// *TODO*
	// this function when call will check to see
	// which pages are used and which are free
	// this function needs to be changed to reflect
	//  first - fit , best - fit, and worst - fit
	// jpj9668
	//coremap->Print();
	coremap->CheckEmptyArray();
	FindVar = coremap->FirstFitFunc(numPages);
	
	//if(FindVar == numPages+1)
	//{
	//	printf("there is not a section large enough to fit the file");
	//}
	printf("FindVar = %d\n",FindVar);
	
	// here FindVar is used to set the physical page  
	// jpj9668
    printf("next variable space %d \n",FindVar);
    for (i = 0;i < numPages; i++) 
    {
	pageTable[i].virtualPage = i;
	pageTable[i].physicalPage = FindVar+i;	// for now, virtual page # = phys page #
	coremap->Mark(FindVar+i);
	pageTable[i].valid = TRUE;
	pageTable[i].use = FALSE;
	pageTable[i].dirty = FALSE;
	pageTable[i].readOnly = FALSE;  
	
	
    };
   
    
    coremap->Print();

// zero out the entire address space, to zero the unitialized data segment 
// and the stack segment
	
// zero the uninitalized data segment 
// jpj9668
// equation of (noffH.uninitData.inFileAddr)+(PageSize*FindVar)) developed
// Eric Killen
	memset(&(machine->mainMemory[((noffH.uninitData.inFileAddr)+(PageSize*FindVar))]),0, size);

// not sure if this is needed yet
// jpj9668	
/*	
// clear out the coremap for the bits used
//jpj9668
	for (i = 0; i < numPages; i++)
	{
		coremap->Clear(FindVar+i);
	};
	coremap->Print();
*/
// equation of (noffH.uninitData.inFileAddr)+(PageSize*FindVar)) developed
// Eric Killen
	machineLocation = (&(machine->mainMemory[(noffH.uninitData.inFileAddr)+(PageSize*FindVar)]));
// then, copy in the code and data segments into memory
// here noffH.code.inFileAddr (which is the code  segment)
// is added to the FindVar variable offset so
// that the correct physical memory will be used when writing to main memory
// jpj9668
// equation of (noffH.uninitData.inFileAddr)+(PageSize*FindVar)) developed
// Eric Killen
	printf(" line 226 in addrspace.cc\n");
	//printf(" FindVar %d, noffH.code.virtualAddr %d",
	  if (noffH.code.size > 0) {
        DEBUG('a', "Initializing code segment, at 0x%x, size %d\n", 
			((noffH.code.virtualAddr)+(PageSize*FindVar)), noffH.code.size);
        executable->ReadAt(&(machine->mainMemory[(noffH.code.virtualAddr)+(PageSize*FindVar)]),
			noffH.code.size, noffH.code.inFileAddr);
    }
    if (noffH.initData.size > 0) {
        DEBUG('a', "Initializing data segment, at 0x%x, size %d\n", 
			((noffH.initData.virtualAddr)+(PageSize*FindVar)), noffH.initData.size);
        executable->ReadAt(&(machine->mainMemory[noffH.initData.virtualAddr]),
			noffH.initData.size, noffH.initData.inFileAddr);
    }
}


//----------------------------------------------------------------------
// AddrSpace::~AddrSpace
// 	Dealloate an address space.  Nothing for now!
//----------------------------------------------------------------------
AddrSpace::~AddrSpace()
{	
   printf(" line 249 in addrspace.cc");
   machine->DumpState();
  
// Here the memory is deallocated so that the other processes may claim it
// jpj9668
	memset(&(machine->mainMemory[(noffH.code.virtualAddr)+(PageSize*FindVar)]),0, size);
	
	printf(" line 256 in addrspace.cc\n");
   	machine->DumpState();
  
// clear out the coremap for the bits used
//jpj9668
	coremap->Print();
	for (unsigned int i = 0; i < numPages; i++)
	{
		coremap->Clear(FindVar+i);
	};
	coremap->Print();
   delete pageTable;
}
// adjust the size of the thread array if it 
// reaches its max size
void sizeChange()
{

	Thread **temp = new Thread*[sizeArray];
	for(int i = 0; i<sizeArray ; i++)
	{
		temp[i] = threadArray[i];
	}
	sizeArray = sizeArray + 10;
	delete threadArray;
	threadArray = new Thread*[sizeArray];
	for(int i = 0; i< sizeArray ; i++)
	{
		threadArray[i] = temp[i];
	}
	delete temp;
	
}

//----------------------------------------------------------------------
// AddrSpace::InitRegisters
// 	Set the initial values for the user-level register set.
//
// 	We write these directly into the "machine" registers, so
//	that we can immediately jump to user code.  Note that these
//	will be saved/restored into the currentThread->userRegisters
//	when this thread is context switched out.
//----------------------------------------------------------------------

//used to check is section is clear for new process
// jpj9668
unsigned int
AddrSpace::TestBitMap(unsigned int pages)
{
    unsigned int a,answer;
	for(a = 0;a<32;a++)
	{
		answer = coremap->Test(a);
		if (TestBitMapPages(pages,a) == TRUE)
		{
			return answer;
		};
	};
	answer = 33;
	return answer;
};
// used to check if all pages are clear of entire process
// jpj9668
bool
AddrSpace::TestBitMapPages(unsigned int pages, int offset)
{
	unsigned int b;
	for(b = 0;b < numPages;b++)
	{
		if(coremap->Test(offset+b) == TRUE)
		{
			return FALSE;
		};
	};
	return TRUE;
};

//----------------------------------------------------------------------
// AddrSpace::InitRegisters
// 	Set the initial values for the user-level register set.
//
// 	We write these directly into the "machine" registers, so
//	that we can immediately jump to user code.  Note that these
//	will be saved/restored into the currentThread->userRegisters
//	when this thread is context switched out.
//----------------------------------------------------------------------

void
AddrSpace::InitRegisters()
{
    int i;

    for (i = 0; i < NumTotalRegs; i++)
	machine->WriteRegister(i, 0);
    for (i = 0; i < NumTotalRegs;i++)
    // Initial program counter -- must be location of "Start"
    machine->WriteRegister(PCReg, 0);	

    // Need to also tell MIPS where next instruction is, because
    // of branch delay possibility
    machine->WriteRegister(NextPCReg, 4);

   // Set the stack register to the end of the address space, where we
   // allocated the stack; but subtract off a bit, to make sure we don't
   // accidentally reference off the end!
    machine->WriteRegister(StackReg, numPages * PageSize - 16);
    DEBUG('a', "Initializing stack register to %d\n", numPages * PageSize - 16);
	printf(" line 353 in addrspace.cc ");
	machine->DumpState();
}

//----------------------------------------------------------------------
// AddrSpace::SaveState
// 	On a context switch, save any machine state, specific
//	to this address space, that needs saving.
//
//	For now, nothing!
//----------------------------------------------------------------------


// this can be done look up what is needed to PCB?
// unfinished
// jpj9668
void AddrSpace::SaveState() 
{

}

//----------------------------------------------------------------------
// AddrSpace::RestoreState
// 	On a context switch, restore the machine state so that
//	this address space can run.
//
//      For now, tell the machine where to find the page table.
//----------------------------------------------------------------------
// work in here for resetting the all information
void AddrSpace::RestoreState() 
{
    machine->pageTable = pageTable;
    machine->pageTableSize = numPages;
}
